package com.zshan.clinic.common.util.attack;

import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * XSS攻击
 */
@Slf4j
public class XssCleanUtil {

    private static final char[][] TEXT = new char[64][];

    static {
        for (int i = 0; i < 64; i++) {
            TEXT[i] = new char[] { (char) i };
        }
        // special HTML characters
        TEXT['\''] = "&#039;".toCharArray(); // 单引号
        TEXT['"'] = "&#34;".toCharArray();   // 双引号
        TEXT['&'] = "&#38;".toCharArray();   // &
        TEXT['<'] = "&#60;".toCharArray();   // <
        TEXT['>'] = "&#62;".toCharArray();   // >
    }

    /**
     * 转义文本中的HTML字符为安全的字符
     *
     * @param text 被转义的文本
     * @return 转义后的文本
     */
    public static String escape(String text) {
        return encode(text);
    }

    /**
     * 还原被转义的HTML特殊字符
     *
     * @param content 包含转义符的HTML内容
     * @return 转换后的字符串
     */
    public static String unescape(String content) {
        return decode(content);
    }

    /**
     * 清除所有HTML标签，但是不删除标签内的内容
     *
     * @param content 文本
     * @return 清除标签后的文本
     */
    public static String clean(String content) {
        /**
         * 方法	说明
         * Safelist.none()	不允许任何标签或属性
         * Safelist.simpleText()	允许基本文本标签，如 <b>, <i>, <u>, <em>, <strong> 等
         * Safelist.basic()	允许常用标签：<a>, <b>, <blockquote>, <br>, <cite>, <code>, <em>, <i>, <li>, <ol>, <p>, <pre>, <strong>, <ul>
         * Safelist.basicWithImages()	与 basic() 类似，但允许 <img>，包括 src、alt、height、width 属性
         * Safelist.relaxed()	最宽容，允许很多 HTML 标签和部分样式
         * Safelist.none().addTags("span")	你可以基于现有规则进一步“添加允许标签/属性”
         */
        if (content == null || content.isEmpty()) {
            return content;
        }

        // 使用 relaxed 或其他 Safelist
        Safelist safelist = Safelist.relaxed();

        // 关键：禁用 prettyPrint，防止 \n 被吃掉
        org.jsoup.nodes.Document.OutputSettings outputSettings = new org.jsoup.nodes.Document.OutputSettings();
        outputSettings.prettyPrint(false);
        return Jsoup.clean(content, "", safelist, outputSettings);
    }


    private static String encode(String text) {
        if (text == null || text.length() == 0) {
            return "";
        }
        StringBuilder buffer = new StringBuilder(text.length() + (text.length() >> 2));
        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            if (c < 64) {
                buffer.append(TEXT[c]);
            } else {
                buffer.append(c);
            }
        }
        return buffer.toString();
    }

    public static String decode(String content) {
        if (content == null || content.isEmpty()) {
            return content;
        }
        StringBuilder tmp = new StringBuilder(content.length());
        int lastPos = 0, pos;
        char ch;
        while (lastPos < content.length()) {
            pos = content.indexOf("%", lastPos);
            if (pos == lastPos) {
                if (content.charAt(pos + 1) == 'u') {
                    ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
                    tmp.append(ch);
                    lastPos = pos + 6;
                } else {
                    ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
                    tmp.append(ch);
                    lastPos = pos + 3;
                }
            } else {
                if (pos == -1) {
                    tmp.append(content.substring(lastPos));
                    lastPos = content.length();
                } else {
                    tmp.append(content.substring(lastPos, pos));
                    lastPos = pos;
                }
            }
        }
        return tmp.toString();
    }

    /**
     * 过滤XSS攻击，递归清理Map中的内容
     *
     * @param param 输入Map参数
     * @return 清理后的Map
     */
    public static Map<String, Object> cleanMapXSS(Map<String, Object> param) {
        Map<String, Object> resultMap = new HashMap<>();
        if (param == null) {
            return resultMap;
        }
        for (Map.Entry<String, Object> entry : param.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            if (value instanceof Map) {
                resultMap.put(key, cleanMapXSS((Map<String, Object>) value));
            } else if (value instanceof List) {
                resultMap.put(key, cleanListXSS((List<?>) value));
            } else if (value instanceof String) {
                String strVal = (String) value;
                // 保留 null, "" 和 "null" 不做清理，直接原样放入
                if (strVal != null && !strVal.isEmpty() && !"null".equals(strVal)) {
                    resultMap.put(key, clean(strVal));
                } else {
                    resultMap.put(key, strVal);
                }
            } else {
                resultMap.put(key, value);
            }
        }
        return resultMap;
    }

    private static List<Object> cleanListXSS(List<?> list) {
        List<Object> cleanedList = new ArrayList<>();
        if (list == null) {
            return cleanedList;
        }
        for (Object obj : list) {
            if (obj instanceof Map) {
                cleanedList.add(cleanMapXSS((Map<String, Object>) obj));
            } else if (obj instanceof List) {
                cleanedList.add(cleanListXSS((List<?>) obj));
            } else if (obj instanceof String) {
                String strVal = (String) obj;
                // 保留 null, "" 和 "null" 不做清理
                if (strVal != null && !strVal.isEmpty() && !"null".equals(strVal)) {
                    cleanedList.add(clean(strVal));
                } else {
                    cleanedList.add(strVal);
                }
            } else {
                cleanedList.add(obj);
            }
        }
        return cleanedList;
    }


    public static void main(String[] args) {
        String html = "清淡饮食，忌食辛辣刺激食物。\n忌食油腻、煎炸、烧烤类食物。\n忌食生冷食物及冷饮。\n忌食海鲜、牛羊肉等发物。\n";
        System.out.println("clean: " + clean(html));
        System.out.println("escape: " + escape(html));
        System.out.println("unescape: " + unescape(escape(html)));

        Map<String, Object> param = new HashMap<>();
        param.put("key1", "<script>alert(1)</script>123");
        param.put("key2", "null");
        param.put("key3", "");
        param.put("key4", null);
        List<Object> list = new ArrayList<>();
        list.add("<b>bold</b>");
        list.add("null");
        param.put("list", list);

        Map<String, Object> cleaned = cleanMapXSS(param);
        System.out.println("cleaned map: " + cleaned);
    }

}
